home *** CD-ROM | disk | FTP | other *** search
- /*================================================================================
- DialogUtilities.c
-
- circa 1991 by Greg Anderson
- greggor@apple.com
-
- FREE DISTRIBUTION--use and enjoy
-
- This file contains various and sundry dialog box utility routines
- ================================================================================*/
- #include "DialogUtilities.h"
-
- #ifndef __TYPES__
- #include <Types.h>
- #endif
- #ifndef __SYSEQU__
- #include <SysEqu.h>
- #endif
- #ifndef __TOOLUTILS__
- #include <ToolUtils.h>
- #endif
- #ifndef __ERRORS__
- #include <Errors.h>
- #endif
- #ifndef __MEMORY__
- #include <Memory.h>
- #endif
- #ifndef __RESOURCES__
- #include <Resources.h>
- #endif
- #ifndef __QUICKDRAW__
- #include <Quickdraw.h>
- #endif
- #ifndef __CONTROLS__
- #include <Controls.h>
- #endif
- #ifndef __DIALOGS__
- #include <Dialogs.h>
- #endif
- #ifndef __SCRAP__
- #include <Scrap.h>
- #endif
-
- /*----------------------------------------------------------------------
- MessageBox
-
- Display a message
- ----------------------------------------------------------------------*/
- void MessageBox( Str255 pstr )
- {
- DialogPtr dlog;
- short itemHit;
-
- dlog = GetNewDialog( 150, (Ptr)0L, (WindowPtr)-1L);
-
- if( dlog != nil )
- {
- SetPort( dlog );
- ParamText( pstr, nil, nil, nil );
- SelectWindow(dlog);
- InstallDefaultOutline( dlog, 1 );
- CenterAndShowDialog(dlog);
- /*
- // Wait for user to click "Okay"
- */
- do
- {
- ModalDialog((ModalFilterProcPtr) CutPasteFilter, &itemHit);
- } while( itemHit != 1 );
- DisposDialog(dlog);
- }
- else
- {
- DebugStr( pstr );
- }
- }
-
- /*----------------------------------------------------------------------
- CenterAndShowDialog
-
- Center the specified dialog box & show it
-
- This code centers the dialog box on the main monitor (the one
- with the menu bar) such that 1/3rd of the empty space left on
- that screen is above the dialog and 2/3rds of it is below the
- dialog
-
- Note:
-
- This routine looks at MBarHeight to correctly calculate the
- horizontal position of the dialog box. Accessing low memory
- globals is generally an evil thing to do, but in this case
- there is no good alternative.
- ----------------------------------------------------------------------*/
- void CenterAndShowDialog(DialogPtr dlog)
- {
- short menuHeight;
- short dlogWidth;
- short dlogHeight;
- short scrnWidth;
- short scrnHeight;
- short newDlogX;
- short newDlogY;
-
- /*
- // Look at the low memory global 'MBarHeight' to determine the
- // height of the menu bar (in pixels)
- */
- menuHeight = *((short*)MBarHeight);
-
- /*
- // Calculate the size of the dialog box and the main screen
- // (without the menu bar)
- */
- dlogWidth = (dlog->portRect.right - dlog->portRect.left);
- dlogHeight = (dlog->portRect.bottom - dlog->portRect.top);
- scrnWidth = (qd.screenBits.bounds.right - qd.screenBits.bounds.left);
- scrnHeight = (qd.screenBits.bounds.bottom - qd.screenBits.bounds.top) - menuHeight;
- /*
- // Quick check: don't leave any empty space if the dialog is
- // too large to fit on the main screen. This sanity check
- // really should not be necessary, though, as all dialog boxes
- // should fit on 9" screens
- */
- if( dlogHeight > scrnHeight)
- dlogHeight = scrnHeight;
- /*
- // Calculate the menu's new location
- */
- newDlogX = qd.screenBits.bounds.left + (scrnWidth - dlogWidth) / 2;
- newDlogY = qd.screenBits.bounds.top + menuHeight + (scrnHeight - dlogHeight) / 3;
- /*
- // Move the dialog and show it
- */
- MoveWindow(dlog,newDlogX,newDlogY,false);
- ShowWindow(dlog);
- }
-
- /*----------------------------------------------------------------------
- AddNewUserItem
-
- Creates a new useritem in the specified dialog box & returns
- its item number
- ----------------------------------------------------------------------*/
- short AddNewUserItem( DialogPtr dlog )
- {
- DialogPeek theDialog = (DialogPeek)dlog;
- short** itemHandle = (short**)theDialog->items;
- short nItems = **itemHandle + 1;
- short newItem = 0;
- DITLitem* ditlPtr;
- Size itemHandleSize;
-
- itemHandleSize = GetHandleSize( (Handle)itemHandle );
- SetHandleSize( (Handle)itemHandle, itemHandleSize + sizeof(DITLitem) );
- if( MemError() == noErr )
- {
- /*
- // We dereference the (potentially) unlocked itemHandle
- // here; don't do any memory-moving calls until we've
- // initialized the new DITL item
- */
- ditlPtr = (DITLitem*) ( (*itemHandle) + (itemHandleSize / sizeof(short)) );
- /*
- // Fill in the new fields
- */
- ditlPtr->placeholder = 0;
- ditlPtr->itemType = userItem;
- ditlPtr->extraLength = 0;
- /*
- // Remember / record the new number of items
- */
- newItem = nItems + 1;
- **itemHandle = newItem - 1;
- }
-
- return newItem;
- }
-
- /*----------------------------------------------------------------------
- DrawDottedLineProc
-
- Draw a dotted line
- ----------------------------------------------------------------------*/
- pascal void DrawDottedLineProc(DialogPtr dlog, short item)
- {
- short type;
- Handle itemHandle;
- Rect box;
- PenState saveState;
- Point lineStart;
- Point lineEnd;
-
- /*
- // Save the drawing mode before doing anything with
- // the pen
- */
- GetPenState( &saveState );
- /*
- // Get the bounding box of the userItem & set lineStart
- // to the upper left corner and lineEnd to the lower right
- // corner of the bounding rectangle
- */
- GetDItem(dlog, item, &type, (Handle*)&itemHandle, &box);
- lineStart.h = box.left;
- lineStart.v = box.top;
- lineEnd.h = box.right;
- lineEnd.v = box.bottom;
- /*
- // The line is always drawn along the longer edge
- */
- if( (lineEnd.h - lineStart.h) > (lineEnd.v - lineStart.v) )
- lineEnd.v = lineStart.v;
- else
- lineEnd.h = lineStart.h;
- /*
- // Set the pen mode and draw the line
- */
- PenNormal();
- PenPat(qd.gray);
- MoveTo( lineStart.h, lineStart.v );
- LineTo( lineEnd.h, lineEnd.v );
- /*
- // Restore the pen state
- */
- SetPenState( &saveState );
- }
-
- /*----------------------------------------------------------------------
- SetUserItemToDottedLine
-
- Make the given userItem a dotted line
- ----------------------------------------------------------------------*/
- void SetUserItemToDottedLine( DialogPtr dlog, short whichItem )
- {
- short type;
- Handle itemHandle;
- Rect box;
-
- GetDItem(dlog,whichItem,&type,&itemHandle,&box);
- SetDItem(dlog,whichItem,type,(Handle)DrawDottedLineProc,&box);
- }
-
- /*----------------------------------------------------------------------
- DrawFrameRectProc
-
- Draw a frame around the specified userItem
- ----------------------------------------------------------------------*/
- pascal void DrawFrameRectProc(DialogPtr dlog, short item)
- {
- short type;
- Handle itemHandle;
- Rect box;
- PenState saveState;
-
- /*
- // Save the drawing mode before doing anything with
- // the pen
- */
- GetPenState( &saveState );
- /*
- // Get the bounding box of the userItem
- */
- GetDItem(dlog, item, &type, (Handle*)&itemHandle, &box);
- /*
- // Set the pen mode and draw the box
- */
- PenNormal();
- FrameRect( &box );
- /*
- // Restore the pen state
- */
- SetPenState( &saveState );
- }
-
- /*----------------------------------------------------------------------
- SetUserItemToFrameRect
-
- Make the given userItem a dotted line
- ----------------------------------------------------------------------*/
- void SetUserItemToFrameRect( DialogPtr dlog, short whichItem )
- {
- short type;
- Handle itemHandle;
- Rect box;
-
- GetDItem(dlog,whichItem,&type,&itemHandle,&box);
- SetDItem(dlog,whichItem,type,(Handle)DrawFrameRectProc,&box);
- }
-
- /*----------------------------------------------------------------------
- DrawDefaultProc
-
- Draw the thick rounded rectangle around the default button
-
- This routine uses Keith Rollin's algorithm, as presented in the
- USENET Guide to Programming the Macintosh. I have modified the
- basic algorithm only slightly--I add two to the calculated
- 'buttonOval' value. This gets better results, particularly for
- buttons of the default size (18 points).
- ----------------------------------------------------------------------*/
- pascal void DrawDefaultProc(DialogPtr dlog, short item)
- {
- short defaultButton;
-
- /*
- // Don't call GetDItem if the default button # has a strange value
- */
- defaultButton = ((DialogPeek)dlog)->aDefItem;
- if( defaultButton > 0 )
- {
- short type;
- Handle userHandle;
- Rect outlineBox;
-
- /*
- // Only draw the bold outline around the default button
- // if it really is a button
- */
- GetDItem(dlog, defaultButton, &type, &userHandle, &outlineBox);
- if( type == ctrlItem + btnCtrl )
- {
- PenState saveState;
- short buttonOval;
-
- GetPenState( &saveState );
- InsetRect(&outlineBox,-4,-4);
- /*
- // We want to draw the thick line with a normal
- // pen pattern that is 3 pixels wide
- */
- PenNormal();
- PenSize(3,3);
- /*
- // If the button we are outlining is disabled,
- // draw the outline with a gray pattern.
- */
- if( !ButtonEnabled(dlog, defaultButton ) )
- {
- PenPat(qd.gray);
- }
- /*
- // Calculate the curvature to use and draw the thick line
- */
- buttonOval = 2 + (outlineBox.bottom - outlineBox.top) / 2;
- FrameRoundRect(&outlineBox,buttonOval,buttonOval);
-
- SetPenState( &saveState );
- }
- }
- }
-
- /*----------------------------------------------------------------------
- This function sets the dialog item that the default button is
- drawn around. The filterproc (below) does the drawing.
- ----------------------------------------------------------------------*/
- short InstallDefaultOutline(DialogPtr dlog, short button)
- {
- ((DialogPeek)dlog)->aDefItem = button;
-
- return button;
- }
-
- /*----------------------------------------------------------------------
- Draw the thick square rectangle around the dialog item that accepts
- keyboard input (see the System 7 Chooser for an example).
- ----------------------------------------------------------------------*/
- pascal void DrawActiveItemProc(DialogPtr dlog, short item)
- {
- short type;
- Handle itemHandle;
- Rect box;
- PenState saveState;
-
- GetPenState( &saveState );
- GetDItem(dlog, item, &type, &itemHandle, &box);
- PenNormal();
- PenSize(2,2);
- FrameRect(&box);
- SetPenState( &saveState );
- }
-
- /*----------------------------------------------------------------------
- This function creates a useritem around the specified button & installs
- a drawing proc that draws the default border around it.
- ----------------------------------------------------------------------*/
- short InstallActiveItemOutline(DialogPtr dlog, short button)
- {
- short userItem;
- short type;
- Handle item;
- Rect box;
- Rect userBox;
-
- userItem = AddNewUserItem( dlog );
- if( userItem > 0 )
- {
- GetDItem(dlog, button, &type, &item, &box);
- InsetRect(&box,-4,-4);
- GetDItem(dlog, userItem, &type, &item, &userBox);
- SetDItem(dlog, userItem, type, (Handle)DrawActiveItemProc, &box );
- }
-
- return userItem;
- }
-
- /*----------------------------------------------------------------------
- MoveOutline
-
- Move a userItem around the appropriate button.
-
- This function should be used in conjunction with
- InstallDefaultOutline to move the default button indicator
- from one button to another. If you are using the
- CutPasteFilter with this routine, Return and Enter will
- automatically be translated to the correct button
- ----------------------------------------------------------------------*/
- void MoveOutline(DialogPtr dlog, short userItem, short button)
- {
- short type;
- Handle item;
- Rect newBox;
- Rect oldBox;
-
- GetDItem(dlog, ((DialogPeek)dlog)->aDefItem, &type, &item, &oldBox);
- InsetRect(&oldBox,-4,-4);
- GetDItem(dlog, button, &type, &item, &newBox);
- InsetRect(&newBox,-4,-4);
- /*
- // Erase the old box & invalidate the old and new
- // locations to force a redraw
- */
- EraseRect( &oldBox );
- InvalRect( &oldBox );
- InvalRect( &newBox );
- /*
- // Remember where the default button outline moved to
- */
- ((DialogPeek)dlog)->aDefItem = button;
- }
-
- /*----------------------------------------------------------------------
- MoveActiveIndicator
-
- Move the active item indicator to another item.
-
- Note: This routine erases and redraws the active item.
- It would be better to erase & invalidate, but that's
- pretty slow.
- ----------------------------------------------------------------------*/
- void MoveActiveIndicator(DialogPtr dlog, short userItem, short button)
- {
- short type;
- Handle item;
- Rect box;
- Rect userBox;
- RgnHandle badRgn;
- RgnHandle tmpRgn;
-
- GetDItem(dlog, button, &type, &item, &box);
- InsetRect(&box,-4,-4);
- GetDItem(dlog, userItem, &type, &item, &userBox);
- SetDItem(dlog, userItem, type, item, &box );
- /*
- // Erase the old active item indicator
- */
- badRgn = NewRgn();
- tmpRgn = NewRgn();
- RectRgn( badRgn, &userBox );
- InsetRect( &userBox, 2, 2 );
- RectRgn( tmpRgn, &userBox );
- DiffRgn( badRgn, tmpRgn, badRgn );
- EraseRgn( badRgn );
- InvalRgn( badRgn );
- DisposeRgn( badRgn );
- DisposeRgn( tmpRgn );
- /*
- // Draw the new one
- */
- DrawActiveItemProc( dlog, userItem );
- }
-
- /*----------------------------------------------------------------------
- MoveDItem
-
- Move a Dialog Item.
-
- This is not _quite_ as easy as it sounds, because if the item
- is a control (such as a check box or radio button), MoveControl
- must also be called.
-
- NOTE: This routine doesn't quite work, because it does not
- erase or redraw the item that moved unless it is
- a control. Tsk tsk.
- ----------------------------------------------------------------------*/
- void MoveDItem( DialogPtr dlog, short itemNumber, short h, short v )
- {
- Handle itemHandle;
- short itemType;
- Rect box;
-
- /*
- // Get some useful information about the DItem to move
- */
- GetDItem( dlog, itemNumber, &itemType, (Handle*)&itemHandle, &box );
- /*
- // Move the bounding box to the correct location
- */
- box.bottom += (v - box.top);
- box.right += (h - box.left);
- box.top = v;
- box.left = h;
- /*
- // Set the bounding box of the item to move
- */
- SetDItem( dlog, itemNumber, itemType, (Handle)itemHandle, &box );
- /*
- // If the item is a control, call MoveControl
- */
- if( (itemType & ctrlItem) != 0 )
- {
- MoveControl( (ControlHandle)itemHandle, h, v );
- }
- }
-
- /*----------------------------------------------------------------------
- SetItemHandle
-
- Set the item handle of a dialog item (particularly useful for
- useritems)
- ----------------------------------------------------------------------*/
- void SetItemHandle( DialogPtr dlog, short whichItem, Handle newItem )
- {
- short type;
- Handle itemHandle;
- Rect box;
-
- GetDItem(dlog,whichItem,&type,&itemHandle,&box);
- SetDItem(dlog,whichItem,type,newItem,&box);
- }
-
- /*----------------------------------------------------------------------
- GetItemPoint
-
- Returns the location of a given dialog item
- ----------------------------------------------------------------------*/
- Point GetItemPoint( DialogPtr dlog, short itemNum )
- {
- Point loc;
- short type;
- Handle item;
- Rect box;
-
- GetDItem(dlog,itemNum, &type, &item, &box);
- loc.h = box.left;
- loc.v = box.top;
- return( loc );
- }
-
- /*----------------------------------------------------------------------
- ButtonEnabled
-
- This routine is used by DrawDefaultProc and CutPasteFilter
- to determine if the default button is enabled
- ----------------------------------------------------------------------*/
- Boolean ButtonEnabled(DialogPtr dlog, short item)
- {
- ControlHandle buttonHandle = nil;
- Rect buttonBox;
- short type;
-
- GetDItem(dlog, item, &type, (Handle*)&buttonHandle, &buttonBox);
- if( (buttonHandle == nil) || ((type & ctrlItem) == 0) )
- return false;
-
- /*
- // A contrlHilite value of 255 indicates that the button
- // is disabled
- */
- return ( (*buttonHandle)->contrlHilite != 255 );
- }
-
- /*----------------------------------------------------------------------
- EnableButton
-
- This simple routine will enable or disable a button
- ----------------------------------------------------------------------*/
- pascal void EnableButton(DialogPtr dlog, short button,Boolean enable)
- {
- ControlHandle buttonHandle;
- short type;
- Rect box;
-
- GetDItem(dlog,button,&type,(Handle*)&buttonHandle,&box);
- HiliteControl(buttonHandle, enable ? 0 : 255);
- }
-
- /*----------------------------------------------------------------------
- FlashDlogItem
-
- Momentarily hilite a dialog button
- ----------------------------------------------------------------------*/
- void FlashDlogItem( DialogPtr dlog, short itemNum )
- {
- ControlHandle itemHandle;
- short itemType;
- Rect iRect;
- long ticky;
-
- GetDItem(dlog,itemNum,&itemType,(Handle*)&itemHandle,&iRect);
- HiliteControl(itemHandle,1);
- Draw1Control(itemHandle);
- ticky = TickCount() + 10;
- while( ticky > TickCount() );
- HiliteControl(itemHandle,0);
- Draw1Control(itemHandle);
- }
-
- /*----------------------------------------------------------------------
- CutPasteFilter
-
- This filter, when passed to ModalDialog, will allow the user to
- cut, copy and paste by pressing command-X, command-C or command-V,
- respectively. It also checks if the cursor is over an editText
- item, and if so, the cursor is changed into an IBeam.
-
- This procedure may be used as the filterProc of any dialog that
- uses ModelDialog(). Note that this filter calls FlashDlogItem(),
- above. In all other respects, it is completely self-contained.
-
- This routine always translates command-. to button #2.
- Your dialog box should have button #2 set up as a
- 'Cancel' button if you use this routine.
- ----------------------------------------------------------------------*/
- pascal Boolean CutPasteFilter( DialogPtr dlog, EventRecord* event, short* item )
- {
- DialogRecord *dp = (DialogRecord *)dlog;
- TEHandle te;
- Str255 Pstr;
- long num;
- char key;
- char code;
- short itemType = statText;
- short itemNum;
- Handle itemHandle;
- Point mouse;
- Rect tRect;
-
- te = dp->textH;
- /*
- // Adjust the cursor to an iBeam or Arrow as appropriate
- */
- GetMouse( &mouse );
- itemNum = FindDItem(dlog,mouse) + 1;
- if( itemNum > 0 )
- GetDItem(dlog,itemNum,&itemType,&itemHandle,&tRect);
- SetCursor( (itemType == editText) ? *GetCursor(iBeamCursor) : &qd.arrow);
-
- /*
- // If the event is an update event, check to see
- // if it is for this dialog or for some other window
- */
- if( (event->what == updateEvt) )
- {
- /*
- // If the update event is for this dialog box,
- // then draw the default button
- */
- if( StripAddress((DialogPtr)event->message) == StripAddress(dlog) )
- {
- /*
- // The 'item' parameter is ignored, but we
- // don't have any better value to pass to
- // it than the item # of the default button
- */
- DrawDefaultProc(dlog, ((DialogPeek)dlog)->aDefItem );
- }
- }
-
- /*
- // Is the event a key-down event?
- */
- if( (event->what == keyDown) || (event->what == autoKey) )
- {
- key = (event->message & charCodeMask);
- code = (event->message & keyCodeMask) >> 8;
- /*
- // If F2, F3 or F4 (cut, copy or paste) are pressed,
- // forge a Command-X, C or V and handle the appropriate
- // command below.
- */
- switch( code )
- {
- case 120:
- {
- event->modifiers |= cmdKey;
- key = 'x';
- break;
- }
-
- case 99:
- {
- event->modifiers |= cmdKey;
- key = 'c';
- break;
- }
-
- case 118:
- {
- event->modifiers |= cmdKey;
- key = 'v';
- break;
- }
- }
- /*
- // Is the command key down?
- */
- if( event->modifiers & cmdKey )
- {
- switch( key )
- {
- /*
- // Command-X, C and V are cut, copy and paste.
- */
- case 'x':
- case 'c':
- {
- ZeroScrap();
- if( key == 'x' )
- DlgCut( dlog );
- else
- DlgCopy( dlog );
- if( TEToScrap() != noErr )
- SysBeep(120);
- return true;
- }
-
- case 'v':
- {
- if( TEFromScrap() == noErr )
- DlgPaste( dlog );
- else
- SysBeep(120);
- return true;
- }
-
- /*
- // Command-. cancels
- */
- case '.':
- {
- *item = 2;
- FlashDlogItem( dlog, *item );
- return true;
- }
- }
- /*
- // All other command-key combinations do nothing
- */
- event->what = nullEvent;
- return false;
- }
- /*
- // If RETURN or ENTER was pressed, exit with
- // itemHit = ((DialogPeek)dlog)->aDefItem--but only if the
- // default button is enabled.
- */
- if( ((key == 13) || (key == 3)) && ButtonEnabled(dlog,((DialogPeek)dlog)->aDefItem) )
- {
- *item = ((DialogPeek)dlog)->aDefItem;
- FlashDlogItem( dlog, *item );
- return true;
- }
- /*
- // Swallow non-printing characters
- */
- if( (key < ' ') && (key != 8) && (key != 9) )
- {
- event->what = nullEvent;
- return false;
- }
- }
- return false;
- }
-